﻿using SuperMap.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MDF
{
    class MatchAttribute
    {
        //语义树的读取对象
        private IO_xml m_xml;
        /// <summary>
        /// 已匹配的字段信息
        /// </summary>
        public List<string[]> FieldResult;
        /// <summary>
        /// 初始化构造
        /// </summary>
        public MatchAttribute()
        {
            //统计进度
            DatasetsMatchResult.AttributeProgressCount = 0;
            DatasetsMatchResult.AttributeProgressValue = 0;
            m_xml = new IO_xml("ShuXingZiDian");
        }
        /// <summary>
        /// 初始化构造
        /// </summary>
        /// <param name="IsStart">是否初始化进度</param>
        public MatchAttribute(bool IsStart)
        {
            m_xml = new IO_xml("ShuXingZiDian");
            if (IsStart)
            {
                //统计进度
                DatasetsMatchResult.AttributeProgressCount = 0;
                DatasetsMatchResult.AttributeProgressValue = 0;
            }
        }
        /// <summary>
        /// 属性数据匹配
        /// </summary>
        /// <param name="reSet1">记录集1</param>
        /// <param name="reSet2">记录集2</param>
        /// <param name="fieldInfos">已映射字段</param>
        /// <param name="sameSets">同义词集</param>
        /// <returns>返回属性匹配的结果列表，其单个元素形式为double[] { geoPoint1.ID, geoPoint2.ID, Sim }</returns>
        public List<double[]> MatchDataset(Recordset reSet1, Recordset reSet2, List<string[]> fieldInfos, List<string[]> sameSets)
        {
            try
            {
                List<double[]> Sims = new List<double[]>();
                //判断是否有映射字段
                if (fieldInfos.Count > 0)
                {
                    //保存映射字段
                    FieldResult = fieldInfos;
                    //将映射字段转为对应的数据集的字段序号值
                    List<int[]> fieldIndex1AB = GetFieldIndexAB(reSet1.GetFieldInfos(), fieldInfos, 0);
                    List<int[]> fieldIndex2AB = GetFieldIndexAB(reSet2.GetFieldInfos(), fieldInfos, 1);
                    //匹配非映射字段
                    List<int[]> newIndex1_2B = MatchOtherInfos(reSet1.GetFieldInfos(), reSet2.GetFieldInfos(), fieldIndex1AB[1], fieldIndex2AB[1], sameSets);
                    fieldIndex1AB.Insert(1, newIndex1_2B[0]);
                    fieldIndex2AB.Insert(1, newIndex1_2B[1]);

                    //在进行拆分的相似性计算
                    reSet1.MoveFirst();
                    for (int i = 0; i < reSet1.RecordCount; i++)
                    {
                        //获取记录集1当前记录的属性值
                        List<object[]> values1AB = GetValuesAB(reSet1.GetValues(), fieldIndex1AB);
                        //获取当前记录的属性值
                        reSet2.MoveFirst();
                        for (int j = 0; j < reSet2.RecordCount; j++)
                        {
                            //获取记录集2当前记录的属性值
                            List<object[]> values2AB = GetValuesAB(reSet2.GetValues(), fieldIndex2AB);
                            //计算相似性（映射字段数和非映射字段数，作为各自相似性的权重）
                            double simA = GetSimAB(values1AB[0], values2AB[0], sameSets);
                            double simB = GetSimAB(values1AB[1], values2AB[1], sameSets);
                            double sim = (values1AB[0].Length * simA + values1AB[1].Length * simB) / (values1AB[0].Length + values1AB[1].Length);
                            //记录结果
                            Sims.Add(new double[] { reSet1.GetID(), reSet2.GetID(), sim });
                            reSet2.MoveNext();
                        }
                        reSet1.MoveNext();
                    }
                    //返回结果
                    return Sims;
                }
                else
                {
                    //没有字段映射直接匹配字段
                    FieldResult = new List<string[]>();
                    //获取非系统字段的序号
                    List<int[]> fieldIndex1B = GetFieldIndexAB(reSet1.GetFieldInfos(), fieldInfos, -1);
                    List<int[]> fieldIndex2B = GetFieldIndexAB(reSet2.GetFieldInfos(), fieldInfos, -1);
                    //匹配非映射字段
                    List<int[]> newIndex1_2B = MatchOtherInfos(reSet1.GetFieldInfos(), reSet2.GetFieldInfos(), fieldIndex1B[1], fieldIndex2B[1], sameSets);
                    fieldIndex1B.Insert(1, newIndex1_2B[0]);
                    fieldIndex2B.Insert(1, newIndex1_2B[1]);
                    //在进行拆分的相似性计算
                    reSet1.MoveFirst();
                    for (int i = 0; i < reSet1.RecordCount; i++)
                    {
                        //获取记录集1当前记录的属性值
                        List<object[]> values1AB = GetValuesAB(reSet1.GetValues(), fieldIndex1B);
                        //获取当前记录的属性值
                        reSet2.MoveFirst();
                        for (int j = 0; j < reSet2.RecordCount; j++)
                        {
                            //获取记录集2当前记录的属性值
                            List<object[]> values2AB = GetValuesAB(reSet2.GetValues(), fieldIndex2B);
                            //计算相似性
                            double simB = GetSimAB(values1AB[1], values2AB[1], sameSets);
                            double sim = simB;
                            //记录结果
                            Sims.Add(new double[] { reSet1.GetID(), reSet2.GetID(), sim });
                            reSet2.MoveNext();
                        }
                        reSet1.MoveNext();
                    }
                    //返回结果
                    return Sims;
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 属性数据匹配
        /// </summary>
        /// <param name="reSet1">记录集1</param>
        /// <param name="reSet2">记录集2</param>
        /// <param name="fieldInfos">已映射字段</param>
        /// <param name="sameSets">同义词集</param>
        /// <param name="Token">取消操作的对象</param>
        /// <returns>返回属性匹配的结果列表，其单个元素形式为double[] { geoPoint1.ID, geoPoint2.ID, Sim }</returns>
        public List<double[]> MatchDataset(Recordset reSet1, Recordset reSet2, List<string[]> fieldInfos, List<string[]> sameSets, CancellationToken Token)
        {
            try
            {
                List<double[]> Sims = new List<double[]>();
                //判断是否有映射字段
                if (fieldInfos.Count > 0)
                {
                    //保存映射字段
                    FieldResult = fieldInfos;
                    //将映射字段转为对应的数据集的字段序号值
                    List<int[]> fieldIndex1AB = GetFieldIndexAB(reSet1.GetFieldInfos(), fieldInfos, 0);
                    List<int[]> fieldIndex2AB = GetFieldIndexAB(reSet2.GetFieldInfos(), fieldInfos, 1);
                    //匹配非映射字段
                    List<int[]> newIndex1_2B = MatchOtherInfos(reSet1.GetFieldInfos(), reSet2.GetFieldInfos(), fieldIndex1AB[1], fieldIndex2AB[1], sameSets);
                    fieldIndex1AB.Insert(1, newIndex1_2B[0]);
                    fieldIndex2AB.Insert(1, newIndex1_2B[1]);

                    //在进行拆分的相似性计算
                    reSet1.MoveFirst();
                    for (int i = 0; i < reSet1.RecordCount; i++)
                    {
                        //获取记录集1当前记录的属性值
                        List<object[]> values1AB = GetValuesAB(reSet1.GetValues(), fieldIndex1AB);
                        //获取当前记录的属性值
                        reSet2.MoveFirst();
                        for (int j = 0; j < reSet2.RecordCount; j++)
                        {
                            if (Token.IsCancellationRequested)
                            {
                                return null;
                            }
                            else
                            {
                                //获取记录集2当前记录的属性值
                                List<object[]> values2AB = GetValuesAB(reSet2.GetValues(), fieldIndex2AB);
                                //计算相似性（映射字段数和非映射字段数，作为各自相似性的权重）
                                double simA = GetSimAB(values1AB[0], values2AB[0], sameSets);
                                double simB = GetSimAB(values1AB[1], values2AB[1], sameSets);
                                double sim = (values1AB[0].Length * simA + values1AB[1].Length * simB) / (values1AB[0].Length + values1AB[1].Length);
                                //记录结果
                                Sims.Add(new double[] { reSet1.GetID(), reSet2.GetID(), sim });
                                reSet2.MoveNext();
                                //记录进度
                                DatasetsMatchResult.AttributeProgressCount++;
                                DatasetsMatchResult.AttributeProgressValue = DatasetsMatchResult.AttributeProgressCount * 100 / DatasetsMatchResult.SetsRecordCountSum;
                            }
                                
                        }
                        reSet1.MoveNext();
                    }
                    //返回结果
                    return Sims;
                }
                else
                {
                    //没有字段映射直接匹配字段
                    FieldResult = new List<string[]>();
                    //获取非系统字段的序号
                    List<int[]> fieldIndex1B = GetFieldIndexAB(reSet1.GetFieldInfos(), fieldInfos, -1);
                    List<int[]> fieldIndex2B = GetFieldIndexAB(reSet2.GetFieldInfos(), fieldInfos, -1);
                    //匹配非映射字段
                    List<int[]> newIndex1_2B = MatchOtherInfos(reSet1.GetFieldInfos(), reSet2.GetFieldInfos(), fieldIndex1B[1], fieldIndex2B[1], sameSets);
                    fieldIndex1B.Insert(1, newIndex1_2B[0]);
                    fieldIndex2B.Insert(1, newIndex1_2B[1]);
                    //在进行拆分的相似性计算
                    reSet1.MoveFirst();
                    for (int i = 0; i < reSet1.RecordCount; i++)
                    {
                        //获取记录集1当前记录的属性值
                        List<object[]> values1AB = GetValuesAB(reSet1.GetValues(), fieldIndex1B);
                        //获取当前记录的属性值
                        reSet2.MoveFirst();
                        for (int j = 0; j < reSet2.RecordCount; j++)
                        {
                            if (Token.IsCancellationRequested)
                            {
                                return null;
                            }
                            else
                            {
                                //获取记录集2当前记录的属性值
                                List<object[]> values2AB = GetValuesAB(reSet2.GetValues(), fieldIndex2B);
                                //计算相似性
                                double simB = GetSimAB(values1AB[1], values2AB[1], sameSets);
                                double sim = simB;
                                //记录结果
                                Sims.Add(new double[] { reSet1.GetID(), reSet2.GetID(), sim });
                                reSet2.MoveNext();
                                //记录进度
                                DatasetsMatchResult.AttributeProgressCount++;
                                DatasetsMatchResult.AttributeProgressValue = DatasetsMatchResult.AttributeProgressCount * 100 / DatasetsMatchResult.SetsRecordCountSum;
                            }    
                        }
                        reSet1.MoveNext();
                    }
                    //返回结果
                    return Sims;
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 匹配非映射字段
        /// </summary>
        /// <param name="setFieldInfos1">记录集1的全部字段</param>
        /// <param name="setFieldInfos2">记录集2的全部字段</param>
        /// <param name="fieldIndex1B">待匹配字段的序号集合1</param>
        /// <param name="fieldIndex2B">待匹配字段的序号集合2</param>
        /// <param name="sameSets"></param>
        /// <returns>新的匹配字段的序号集合，newIndex1B[]+newIndex2B[]的列表</returns>
        private List<int[]> MatchOtherInfos(FieldInfos setFieldInfos1, FieldInfos setFieldInfos2, int[] fieldIndex1B, int[] fieldIndex2B, List<string[]> sameSets)
        {
            try
            {
                //将匹配字段转为对应的数据集的字段序号值
                List<int> newIndex1B = new List<int>();
                List<int> newIndex2B = new List<int>();
                //进行语义匹配
                foreach (int i in fieldIndex1B)
                {
                    double sim = 0;
                    int a = -1, b = -1;
                    foreach (int j in fieldIndex2B)
                    {

                        bool isSame = false;
                        //检索同义词集，没有则进行语义识别
                        foreach (string[] sameSet in sameSets)
                        {
                            if (sameSet.Contains(setFieldInfos1[i].Name) && sameSet.Contains(setFieldInfos2[j].Name))
                            {
                                //是同义词,则匹配，跳出循环
                                a = i;
                                b = j;
                                isSame = true;
                                break;
                            }
                        }
                        if (!isSame && setFieldInfos1[i].Type == setFieldInfos2[j].Type)
                        {
                            //转成单个字符数组
                            char[] chars1 = setFieldInfos1[i].Name.ToArray();
                            char[] chars2 = setFieldInfos2[j].Name.ToArray();
                            //语义相似性计算
                            //统计两个字符数组交集，差集
                            int inCount = chars1.Intersect(chars2).Count();
                            int exCount1 = chars1.Except(chars2).Count();
                            int exCount2 = chars2.Except(chars1).Count();
                            //获取语义树深度
                            int level1 = m_xml.ReadLevel(setFieldInfos1[i].Name);
                            int level2 = m_xml.ReadLevel(setFieldInfos2[j].Name);
                            //计算语义深度
                            double leDepth1 = level1 / (double)(level1 + level2);
                            double leDepth2 = level2 / (double)(level1 + level2);
                            //计算语义相似度
                            double Sf = inCount / (inCount + leDepth1 * exCount1 + leDepth2 * exCount2);
                            //只保留最大相似度的一对映射
                            if (Sf > sim)
                            {
                                sim = Sf;
                                a = i;
                                b = j;
                            }
                        }
                    }
                    if (a != -1 && b != -1)
                    {
                        //添加匹配结果的字段序号
                        newIndex1B.Add(a);
                        newIndex2B.Add(b);
                        //添加匹配字段的信息
                        FieldResult.Add(new string[] { setFieldInfos1[a].Name, setFieldInfos2[b].Name });
                    }
                }
                //返回结果
                return new List<int[]> { newIndex1B.ToArray(), newIndex2B.ToArray() };
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 获取映射字段的序号（A）和非系统字段的其他字段的序号（B）
        /// </summary>
        /// <param name="setFieldInfos">记录集的全部字段</param>
        /// <param name="fieldInfos">映射字段信息</param>
        /// <param name="n">映射字段的数组编号</param>
        /// <returns>int[]A+int[]B的列表</returns>
        private List<int[]> GetFieldIndexAB(FieldInfos setFieldInfos, List<string[]> fieldInfos, int n)
        {
            try
            {
                //将映射字段转为对应的数据集的字段序号值
                List<int> fieldIndexA = new List<int>();
                List<int> fieldIndexB = new List<int>();
                if (n != -1)
                {
                    foreach (string[] fieldInfo in fieldInfos)
                    {
                        fieldIndexA.Add(setFieldInfos.IndexOf(fieldInfo[n]));
                    }
                }
                for (int i = 0; i < setFieldInfos.Count; i++)
                {
                    //非系统字段，并且也没有映射的字段的序号，需要进行匹配
                    if (!setFieldInfos[i].IsSystemField && !fieldIndexA.Contains(i))
                    {
                        fieldIndexB.Add(i);
                    }
                }
                return new List<int[]> { fieldIndexA.ToArray(), fieldIndexB.ToArray() };

            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 获取当前记录映射字段的值（A）和非系统字段的其他字段的值（B）
        /// </summary>
        /// <param name="values">当前记录的所有属性值</param>
        /// <param name="fieldIndexAB">映射和匹配字段的序号集合</param>
        /// <returns>object[]A+object[]B的列表</returns>
        private List<object[]> GetValuesAB(object[] values, List<int[]> fieldIndexAB)
        {
            try
            {
                //获取属性值
                List<object> valuesA = new List<object>();
                List<object> valuesB = new List<object>();
                foreach (int index in fieldIndexAB[0])
                {
                    valuesA.Add(values[index]);
                }
                foreach (int index in fieldIndexAB[1])
                {
                    valuesB.Add(values[index]);
                }

                return new List<object[]> { valuesA.ToArray(), valuesB.ToArray() };
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 计算属性值的相似度
        /// </summary>
        /// <param name="values1">记录1的所有属性值（A映射字段）</param>
        /// <param name="values2">记录2的所有属性值（A映射字段）</param>
        /// <param name="sameSets">同义词集</param>
        /// <returns>返回相应的字段部分的相似度</returns>
        private double GetSimAB(object[] values1, object[] values2, List<string[]> sameSets)
        {
            try
            {
                //字段的相似程度（最后需要除以字段的个数）
                double simF = 0;
                //比较字段集合,1和2数据，这是一一对应的
                for (int i = 0; i < values1.Length; i++)
                {
                    //如果有空值则认为相似度为1
                    if (values1[i] == null || values2[i] == null)
                    {
                        simF++;
                    }
                    else
                    {
                        //int型属性（编号，等级等）
                        if (values1[i].GetType() == typeof(int))
                        {
                            if (values1[i] == values2[i])
                            {
                                //相同则为1(simA + 1 )
                                simF++;
                            }
                        }
                        //double型属性(如坐标、长度等，误差不超过最大值的1%)
                        else if (values1[i].GetType() == typeof(double))
                        {
                            if (values2[i].ToString() == "0")
                            {
                                values2[i] = 0.0;
                            }
                            //差比上最大值
                            double rx = Math.Abs((double)values1[i] - (double)values2[i]) / Math.Max((double)values1[i], (double)values2[i]);
                            if (rx <= 0.01)
                            {
                                //相同则为1(simA + 1 )
                                simF++;
                            }
                        }
                        //string形式的其他信息
                        else
                        {
                            bool isSame = false;
                            //检索同义词集，没有则进行语义识别
                            foreach (string[] sameSet in sameSets)
                            {
                                if (sameSet.Contains(values1[i]) && sameSet.Contains(values2[i]))
                                {
                                    //是同义词,simA加一，跳出循环
                                    simF++;
                                    isSame = true;
                                    break;
                                }
                            }
                            //无同义词，进行语义识别
                            if (!isSame)
                            {
                                //转成单个字符数组
                                char[] chars1 = values1[i].ToString().ToArray();
                                char[] chars2 = values2[i].ToString().ToArray();
                                //统计两个字符数组交集，差集
                                int inCount = chars1.Intersect(chars2).Count();
                                int exCount1 = chars1.Except(chars2).Count();
                                int exCount2 = chars2.Except(chars1).Count();
                                //获取语义树深度
                                int level1 = m_xml.ReadLevel(values1[i].ToString());
                                int level2 = m_xml.ReadLevel(values2[i].ToString());
                                //计算语义深度
                                double leDepth1 = level1 / (double)(level1 + level2);
                                double leDepth2 = level2 / (double)(level1 + level2);
                                //计算语义相似度
                                double sim = inCount / (inCount + leDepth1 * exCount1 + leDepth2 * exCount2);
                                simF += sim;
                            }
                        }
                    }

                }
                //返回映射字段计算结果
                return simF / values1.Length;
            }
            catch (Exception)
            {

                throw;
            }
        }
    }
}
